diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/controller/Maimai2ServletController.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/controller/Maimai2ServletController.java index 096c717b..ea994d43 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/controller/Maimai2ServletController.java +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/controller/Maimai2ServletController.java @@ -43,6 +43,7 @@ public class Maimai2ServletController { private final GetUserRegionHandler getUserRegionHandler; private final GetUserChargeHandler getUserChargeHandler; private final GetUserCourseHandler getUserCourseHandler; + private final UploadUserPhotoHandler uploadUserPhotoHandler; public Maimai2ServletController(GetGameSettingHandler getGameSettingHandler, GetGameEventHandler getGameEventHandler, GetGameRankingHandler getGameRankingHandler, GetGameTournamentInfoHandler getGameTournamentInfoHandler, GetTransferFriendHandler getTransferFriendHandler, GetUserActivityHandler getUserActivityHandler, UserLoginHandler userLoginHandler, UserLogoutHandler userLogoutHandler, @@ -50,7 +51,7 @@ public class Maimai2ServletController { GetUserOptionHandler getUserOptionHandler, GetUserItemHandler getUserItemHandler, GetUserExtendHandler getUserExtendHandler, GetUserGhostHandler getUserGhostHandler, GetUserLoginBonusHandler getUserLoginBonusHandler, GetUserMapHandler getUserMapHandler, GetUserFavoriteHandler getUserFavoriteHandler, GetUserCardHandler getUserCardHandler, GetUserMusicHandler getUserMusicHandler, GetUserRatingHandler getUserRatingHandler, GetUserRegionHandler getUserRegionHandler, - GetGameChargeHandler getGameChargeHandler, GetUserChargeHandler getUserChargeHandler, GetUserCourseHandler getUserCourseHandler) { + GetGameChargeHandler getGameChargeHandler, GetUserChargeHandler getUserChargeHandler, GetUserCourseHandler getUserCourseHandler, UploadUserPhotoHandler uploadUserPhotoHandler) { this.getGameSettingHandler = getGameSettingHandler; this.getGameEventHandler = getGameEventHandler; this.getGameRankingHandler = getGameRankingHandler; @@ -77,6 +78,7 @@ public class Maimai2ServletController { this.getGameChargeHandler = getGameChargeHandler; this.getUserChargeHandler = getUserChargeHandler; this.getUserCourseHandler = getUserCourseHandler; + this.uploadUserPhotoHandler = uploadUserPhotoHandler; } // Mandatory for boot @@ -196,10 +198,9 @@ public class Maimai2ServletController { return "{}"; } - // No support @PostMapping("UploadUserPhotoApi") public String uploadUserPhotoHandler(@ModelAttribute Map request) throws JsonProcessingException { - return "{\"returnCode\":1,\"apiName\":\"com.sega.maimai2servlet.api.UploadUserPhotoApi\"}"; + return uploadUserPhotoHandler.handle(request); } @PostMapping("UploadUserPlaylogApi") diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPhotoHandler.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPhotoHandler.java new file mode 100644 index 00000000..67a6db10 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPhotoHandler.java @@ -0,0 +1,70 @@ +package icu.samnyan.aqua.sega.maimai2.handler.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import icu.samnyan.aqua.sega.maimai2.handler.BaseHandler; +import icu.samnyan.aqua.sega.maimai2.model.request.UploadUserPhoto; +import icu.samnyan.aqua.sega.maimai2.model.request.data.UserPhoto; +import icu.samnyan.aqua.sega.util.jackson.BasicMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Base64; +import java.util.Map; + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Component +public class UploadUserPhotoHandler implements BaseHandler { + + private static final Logger logger = LoggerFactory.getLogger(UploadUserPhotoHandler.class); + + private final BasicMapper mapper; + + public UploadUserPhotoHandler(BasicMapper mapper) { + this.mapper = mapper; + } + + @Override + public String handle(Map request) throws JsonProcessingException { + /* + Maimai DX sends splited base64 data for one jpeg image. + So, make a temp file and keep append bytes until last part received. + If finished, rename it to other name so user can keep save multiple score cards in a single day. + */ + + UploadUserPhoto uploadUserPhoto = mapper.convert(request, UploadUserPhoto.class); + UserPhoto userPhoto = uploadUserPhoto.getUserPhoto(); + + long userId = userPhoto.getUserId(); + int trackNo = userPhoto.getTrackNo(); + + int divNumber = userPhoto.getDivNumber(); + int divLength = userPhoto.getDivLength(); + String divData = userPhoto.getDivData(); + + try { + String tmp_filename = userId + "-" + trackNo + ".tmp"; + byte[] imageData = Base64.getDecoder().decode(divData); + Files.write(Paths.get("data/" + tmp_filename), imageData, StandardOpenOption.CREATE, StandardOpenOption.APPEND); + + if (divNumber == (divLength - 1)) { + String filename = userId + "-" + LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + ".jpg"; + Files.move(Paths.get("data/" + tmp_filename), Paths.get("data/" + filename)); + } + } catch (IOException e) { + logger.error("Result: User photo save failed", e); + } + + logger.info("Result: User photo saved"); + + return "{\"returnCode\":1,\"apiName\":\"com.sega.maimai2servlet.api.UploadUserPhotoApi\"}"; + } +} diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java index 4336138d..8cb03ba4 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java @@ -98,6 +98,9 @@ public class UpsertUserAllHandler implements BaseHandler { String userName = new String(newUserData.getUserName()); newUserData.setUserName(userName); + + // Set isNetMember value to 1, which enables some in-game features. + newUserData.setIsNetMember(1); userDataRepository.saveAndFlush(newUserData); } diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/UploadUserPhoto.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/UploadUserPhoto.java new file mode 100644 index 00000000..196a2d39 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/UploadUserPhoto.java @@ -0,0 +1,18 @@ +package icu.samnyan.aqua.sega.maimai2.model.request; + +import icu.samnyan.aqua.sega.maimai2.model.request.data.UserPhoto; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UploadUserPhoto implements Serializable { + private UserPhoto userPhoto; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/data/UserPhoto.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/data/UserPhoto.java new file mode 100644 index 00000000..192a9c37 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/request/data/UserPhoto.java @@ -0,0 +1,26 @@ +package icu.samnyan.aqua.sega.maimai2.model.request.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserPhoto implements Serializable { + private int orderId; + private long userId; + private int divNumber; + private int divLength; + private String divData; + private int placeId; + private String clientId; + private String uploadDate; + private long playlogId; + private int trackNo; +}